// Copyright (C) 2024 EA group inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <cstdint>
#include <type_traits>
#include <utility>
#include <turbo/base/macros.h>
#include <turbo/base/nullability.h>
#include <turbo/meta/type_traits.h>
#include <turbo/utility/internal/status.h>
#include <turbo/memory/aligned_storage.h>
#include <turbo/strings/string_view.h>
#include <turbo/meta/utility.h>

namespace turbo {

    template<typename T>
    class TURBO_MUST_USE_RESULT Result;

    namespace internal_statusor {

        // Detects whether `U` has conversion operator to `Result<T>`, i.e. `operator
        // Result<T>()`.
        template<typename T, typename U, typename = void>
        struct HasConversionOperatorToStatusOr : std::false_type {
        };

        template<typename T, typename U>
        void test(char (*)[sizeof(std::declval<U>().operator turbo::Result<T>())]);

        template<typename T, typename U>
        struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
                : std::true_type {
        };

        // Detects whether `T` is constructible or convertible from `Result<U>`.
        template<typename T, typename U>
        using IsConstructibleOrConvertibleFromStatusOr =
                std::disjunction<std::is_constructible<T, Result<U> &>,
                        std::is_constructible<T, const Result<U> &>,
                        std::is_constructible<T, Result<U> &&>,
                        std::is_constructible<T, const Result<U> &&>,
                        std::is_convertible<Result<U> &, T>,
                        std::is_convertible<const Result<U> &, T>,
                        std::is_convertible<Result<U> &&, T>,
                        std::is_convertible<const Result<U> &&, T>>;

        // Detects whether `T` is constructible or convertible or assignable from
        // `Result<U>`.
        template<typename T, typename U>
        using IsConstructibleOrConvertibleOrAssignableFromStatusOr =
                std::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>,
                        std::is_assignable<T &, Result<U> &>,
                        std::is_assignable<T &, const Result<U> &>,
                        std::is_assignable<T &, Result<U> &&>,
                        std::is_assignable<T &, const Result<U> &&>>;

        // Detects whether direct initializing `Result<T>` from `U` is ambiguous, i.e.
        // when `U` is `Result<V>` and `T` is constructible or convertible from `V`.
        template<typename T, typename U>
        struct IsDirectInitializationAmbiguous
                : public std::conditional_t<
                        std::is_same<turbo::remove_cvref_t<U>, U>::value, std::false_type,
                        IsDirectInitializationAmbiguous<T, turbo::remove_cvref_t<U>>> {
        };

        template<typename T, typename V>
        struct IsDirectInitializationAmbiguous<T, turbo::Result<V>>
                : public IsConstructibleOrConvertibleFromStatusOr<T, V> {
        };

        // Checks against the constraints of the direction initialization, i.e. when
        // `Result<T>::Result(U&&)` should participate in overload resolution.
        template<typename T, typename U>
        using IsDirectInitializationValid = std::disjunction<
                // Short circuits if T is basically U.
                std::is_same<T, turbo::remove_cvref_t<U>>,
                std::negation<std::disjunction<
                        std::is_same<turbo::Result<T>, turbo::remove_cvref_t<U>>,
                        std::is_same<turbo::Status, turbo::remove_cvref_t<U>>,
                        std::is_same<std::in_place_t, turbo::remove_cvref_t<U>>,
                        IsDirectInitializationAmbiguous<T, U>>>>;

        // This trait detects whether `Result<T>::operator=(U&&)` is ambiguous, which
        // is equivalent to whether all the following conditions are met:
        // 1. `U` is `Result<V>`.
        // 2. `T` is constructible and assignable from `V`.
        // 3. `T` is constructible and assignable from `U` (i.e. `Result<V>`).
        // For example, the following code is considered ambiguous:
        // (`T` is `bool`, `U` is `Result<bool>`, `V` is `bool`)
        //   Result<bool> s1 = true;  // s1.ok() && s1.ValueOrDie() == true
        //   Result<bool> s2 = false;  // s2.ok() && s2.ValueOrDie() == false
        //   s1 = s2;  // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`?
        template<typename T, typename U>
        struct IsForwardingAssignmentAmbiguous
                : public std::conditional_t<
                        std::is_same<turbo::remove_cvref_t<U>, U>::value, std::false_type,
                        IsForwardingAssignmentAmbiguous<T, turbo::remove_cvref_t<U>>> {
        };

        template<typename T, typename U>
        struct IsForwardingAssignmentAmbiguous<T, turbo::Result<U>>
                : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {
        };

        // Checks against the constraints of the forwarding assignment, i.e. whether
        // `Result<T>::operator(U&&)` should participate in overload resolution.
        template<typename T, typename U>
        using IsForwardingAssignmentValid = std::disjunction<
                // Short circuits if T is basically U.
                std::is_same<T, turbo::remove_cvref_t<U>>,
                std::negation<std::disjunction<
                        std::is_same<turbo::Result<T>, turbo::remove_cvref_t<U>>,
                        std::is_same<turbo::Status, turbo::remove_cvref_t<U>>,
                        std::is_same<std::in_place_t, turbo::remove_cvref_t<U>>,
                        IsForwardingAssignmentAmbiguous<T, U>>>>;

        template<bool Value, typename T>
        using Equality = std::conditional_t<Value, T, std::negation<T>>;

        template<bool Explicit, typename T, typename U, bool Lifetimebound>
        using IsConstructionValid = std::conjunction<
                Equality<Lifetimebound,
                        type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
                IsDirectInitializationValid<T, U &&>, std::is_constructible<T, U &&>,
                Equality<!Explicit, std::is_convertible<U &&, T>>,
                std::disjunction<
                        std::is_same<T, turbo::remove_cvref_t<U>>,
                        std::conjunction<
                                std::conditional_t<
                                        Explicit,
                                        std::negation<std::is_constructible<turbo::Status, U &&>>,
                                        std::negation<std::is_convertible<U &&, turbo::Status>>>,
                                std::negation<
                                        internal_statusor::HasConversionOperatorToStatusOr<T, U &&>>>>>;

        template<typename T, typename U, bool Lifetimebound>
        using IsAssignmentValid = std::conjunction<
                Equality<Lifetimebound,
                        type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
                std::is_constructible<T, U &&>, std::is_assignable<T &, U &&>,
                std::disjunction<
                        std::is_same<T, turbo::remove_cvref_t<U>>,
                        std::conjunction<
                                std::negation<std::is_convertible<U &&, turbo::Status>>,
                                std::negation<HasConversionOperatorToStatusOr<T, U &&>>>>,
                IsForwardingAssignmentValid<T, U &&>>;

        template<bool Explicit, typename T, typename U>
        using IsConstructionFromStatusValid = std::conjunction<
                std::negation<std::is_same<turbo::Result<T>, turbo::remove_cvref_t<U>>>,
                std::negation<std::is_same<T, turbo::remove_cvref_t<U>>>,
                std::negation<std::is_same<std::in_place_t, turbo::remove_cvref_t<U>>>,
                Equality<!Explicit, std::is_convertible<U, turbo::Status>>,
                std::is_constructible<turbo::Status, U>,
                std::negation<HasConversionOperatorToStatusOr<T, U>>>;

        template<bool Explicit, typename T, typename U, bool Lifetimebound,
                typename UQ>
        using IsConstructionFromStatusOrValid = std::conjunction<
                std::negation<std::is_same<T, U>>,
                Equality<Lifetimebound,
                        type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
                std::is_constructible<T, UQ>,
                Equality<!Explicit, std::is_convertible<UQ, T>>,
                std::negation<IsConstructibleOrConvertibleFromStatusOr<T, U>>>;

        template<typename T, typename U, bool Lifetimebound>
        using IsStatusOrAssignmentValid = std::conjunction<
                std::negation<std::is_same<T, turbo::remove_cvref_t<U>>>,
                Equality<Lifetimebound,
                        type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
                std::is_constructible<T, U>, std::is_assignable<T, U>,
                std::negation<IsConstructibleOrConvertibleOrAssignableFromStatusOr<
                        T, turbo::remove_cvref_t<U>>>>;

        class Helper {
        public:
            // Move type-agnostic error handling to the .cc.
            static void HandleInvalidStatusCtorArg(turbo::Nonnull<Status *>);

            TURBO_NORETURN static void Crash(const turbo::Status &status);
        };

        // Construct an instance of T in `p` through placement new, passing Args... to
        // the constructor.
        // This abstraction is here mostly for the gcc performance fix.
        template<typename T, typename... Args>
        TURBO_ATTRIBUTE_NONNULL(1)
        void PlacementNew(turbo::Nonnull<void *> p, Args &&... args) {
            new(p) T(std::forward<Args>(args)...);
        }

        // Helper base class to hold the data and all operations.
        // We move all this to a base class to allow mixing with the appropriate
        // TraitsBase specialization.
        template<typename T>
        class ResultData {
            template<typename U>
            friend
            class ResultData;

        public:
            ResultData() = delete;

            ResultData(const ResultData &other) {
                if (other.ok()) {
                    MakeValue(*other.storage_.get());
                    MakeStatus();
                } else {
                    MakeStatus(other.status_);
                }
            }

            ResultData(ResultData &&other) noexcept {
                if (other.ok()) {
                    MakeValue(std::move(*other.storage_.get()));
                    MakeStatus();
                } else {
                    MakeStatus(std::move(other.status_));
                }
            }

            template<typename U>
            explicit ResultData(const ResultData<U> &other) {
                if (other.ok()) {
                    MakeValue(*other.storage_.get());
                    MakeStatus();
                } else {
                    MakeStatus(other.status_);
                }
            }

            template<typename U>
            explicit ResultData(ResultData<U> &&other) {
                if (other.ok()) {
                    MakeValue(std::move(*other.storage_.get()));
                    MakeStatus();
                } else {
                    MakeStatus(std::move(other.status_));
                }
            }

            template<typename... Args>
            explicit ResultData(std::in_place_t, Args &&... args) {
                MakeValue(std::forward<Args>(args)...);
                MakeStatus();
            }

            explicit ResultData(const T &value){
                MakeValue(value);
                MakeStatus();
            }

            explicit ResultData(T &&value) {
                MakeValue(std::move(value));
                MakeStatus();
            }

            template<typename U,
                    std::enable_if_t<std::is_constructible<turbo::Status, U &&>::value,
                            int> = 0>
            explicit ResultData(U &&v) : status_(std::forward<U>(v)) {
                EnsureNotOk();
            }

            ResultData &operator=(const ResultData &other) {
                if (this == &other) return *this;
                if (other.ok())
                    Assign(*other.storage_.get());
                else
                    AssignStatus(other.status_);
                return *this;
            }

            ResultData &operator=(ResultData &&other) {
                if (this == &other) return *this;
                if (other.ok())
                    Assign(std::move(*other.storage_.get()));
                else
                    AssignStatus(std::move(other.status_));
                return *this;
            }

            ~ResultData() {
                Clear();
                if (ok()) {
                    status_.~Status();
                } else {
                    status_.~Status();
                }
            }

            template<typename U>
            void Assign(U &&value) {
                if (ok()) {
                    *storage_.get() = std::forward<U>(value);
                } else {
                    MakeValue(std::forward<U>(value));
                    status_ = OkStatus();
                }
            }

            template<typename U>
            void AssignStatus(U &&v) {
                Clear();
                status_ = static_cast<turbo::Status>(std::forward<U>(v));
                EnsureNotOk();
            }

            bool ok() const { return status_.ok(); }

        protected:
            // status_ will always be active after the constructor.
            // We make it a union to be able to initialize exactly how we need without
            // waste.
            // Eg. in the copy constructor we use the default constructor of Status in
            // the ok() path to avoid an extra Ref call.
            union {
                Status status_;
            };

            AlignedStorage<T> storage_;

            void Clear() {
                if (TURBO_LIKELY(ok())) {
                    storage_.destroy();
                }
            }

            void EnsureOk() const {
                if (TURBO_UNLIKELY(!ok())) Helper::Crash(status_);
            }

            void EnsureNotOk() {
                if (TURBO_UNLIKELY(ok())) Helper::HandleInvalidStatusCtorArg(&status_);
            }

            // Construct the value (ie. storage_) through placement new with the passed
            // argument.
            template<typename... Arg>
            void MakeValue(Arg &&... arg) {
                storage_.construct(std::forward<Arg>(arg)...);
            }

            // Construct the status (ie. status_) through placement new with the passed
            // argument.
            template<typename... Args>
            void MakeStatus(Args &&... args) {
                internal_statusor::PlacementNew<Status>(&status_,
                                                        std::forward<Args>(args)...);
            }
        };

        // Helper base classes to allow implicitly deleted constructors and assignment
        // operators in `Result`. For example, `CopyCtorBase` will explicitly delete
        // the copy constructor when T is not copy constructible and `Result` will
        // inherit that behavior implicitly.
        template<typename T, bool = std::is_copy_constructible<T>::value>
        struct CopyCtorBase {
            CopyCtorBase() = default;

            CopyCtorBase(const CopyCtorBase &) = default;

            CopyCtorBase(CopyCtorBase &&) = default;

            CopyCtorBase &operator=(const CopyCtorBase &) = default;

            CopyCtorBase &operator=(CopyCtorBase &&) = default;
        };

        template<typename T>
        struct CopyCtorBase<T, false> {
            CopyCtorBase() = default;

            CopyCtorBase(const CopyCtorBase &) = delete;

            CopyCtorBase(CopyCtorBase &&) = default;

            CopyCtorBase &operator=(const CopyCtorBase &) = default;

            CopyCtorBase &operator=(CopyCtorBase &&) = default;
        };

        template<typename T, bool = std::is_move_constructible<T>::value>
        struct MoveCtorBase {
            MoveCtorBase() = default;

            MoveCtorBase(const MoveCtorBase &) = default;

            MoveCtorBase(MoveCtorBase &&) = default;

            MoveCtorBase &operator=(const MoveCtorBase &) = default;

            MoveCtorBase &operator=(MoveCtorBase &&) = default;
        };

        template<typename T>
        struct MoveCtorBase<T, false> {
            MoveCtorBase() = default;

            MoveCtorBase(const MoveCtorBase &) = default;

            MoveCtorBase(MoveCtorBase &&) = delete;

            MoveCtorBase &operator=(const MoveCtorBase &) = default;

            MoveCtorBase &operator=(MoveCtorBase &&) = default;
        };

        template<typename T, bool = std::is_copy_constructible<T>::value &&
                                    std::is_copy_assignable<T>::value>
        struct CopyAssignBase {
            CopyAssignBase() = default;

            CopyAssignBase(const CopyAssignBase &) = default;

            CopyAssignBase(CopyAssignBase &&) = default;

            CopyAssignBase &operator=(const CopyAssignBase &) = default;

            CopyAssignBase &operator=(CopyAssignBase &&) = default;
        };

        template<typename T>
        struct CopyAssignBase<T, false> {
            CopyAssignBase() = default;

            CopyAssignBase(const CopyAssignBase &) = default;

            CopyAssignBase(CopyAssignBase &&) = default;

            CopyAssignBase &operator=(const CopyAssignBase &) = delete;

            CopyAssignBase &operator=(CopyAssignBase &&) = default;
        };

        template<typename T, bool = std::is_move_constructible<T>::value &&
                                    std::is_move_assignable<T>::value>
        struct MoveAssignBase {
            MoveAssignBase() = default;

            MoveAssignBase(const MoveAssignBase &) = default;

            MoveAssignBase(MoveAssignBase &&) = default;

            MoveAssignBase &operator=(const MoveAssignBase &) = default;

            MoveAssignBase &operator=(MoveAssignBase &&) = default;
        };

        template<typename T>
        struct MoveAssignBase<T, false> {
            MoveAssignBase() = default;

            MoveAssignBase(const MoveAssignBase &) = default;

            MoveAssignBase(MoveAssignBase &&) = default;

            MoveAssignBase &operator=(const MoveAssignBase &) = default;

            MoveAssignBase &operator=(MoveAssignBase &&) = delete;
        };

        TURBO_NORETURN void ThrowBadStatusOrAccess(turbo::Status status);

        // Used to introduce jitter into the output of printing functions for
        // `Result` (i.e. `turbo_stringify` and `operator<<`).
        class StringifyRandom {
            enum BracesType {
                kBareParens = 0,
                kSpaceParens,
                kBareBrackets,
                kSpaceBrackets,
            };

            // Returns a random `BracesType` determined once per binary load.
            static BracesType RandomBraces() {
                static const BracesType kRandomBraces = static_cast<BracesType>(
                        (reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
                return kRandomBraces;
            }

        public:
            static inline std::string_view OpenBrackets() {
                switch (RandomBraces()) {
                    case kBareParens:
                        return "(";
                    case kSpaceParens:
                        return "( ";
                    case kBareBrackets:
                        return "[";
                    case kSpaceBrackets:
                        return "[ ";
                }
                return "(";
            }

            static inline std::string_view CloseBrackets() {
                switch (RandomBraces()) {
                    case kBareParens:
                        return ")";
                    case kSpaceParens:
                        return " )";
                    case kBareBrackets:
                        return "]";
                    case kSpaceBrackets:
                        return " ]";
                }
                return ")";
            }
        };

    }  // namespace internal_statusor
}  // namespace turbo
